Skip to content

feat: Implement ReconcileManifest RPC for drift detection#1696

Merged
bjcoombs merged 2 commits intodevelopfrom
045-manifest-source-of-truth--15--reconcile-manifest-rpc
Mar 16, 2026
Merged

feat: Implement ReconcileManifest RPC for drift detection#1696
bjcoombs merged 2 commits intodevelopfrom
045-manifest-source-of-truth--15--reconcile-manifest-rpc

Conversation

@bjcoombs
Copy link
Copy Markdown
Collaborator

Summary

  • Add ReconcileManifest RPC to ManifestHistoryService that compares a stored manifest version against live service state and reports drift as structured output (MISSING/MODIFIED/EXTRA)
  • Reuses existing ExportService for live state collection and ManifestDiffer for comparison, mapping diff actions to drift types
  • Wire ReconcileService into service registration when ExportCollectors are available
  • Read-only safety-net operation with no auto-repair (Phase 1 per PRD)

Changes

File Change
manifest_history_service.proto Add ReconcileManifest RPC, DriftItem, DriftType enum, ReconcileSummary messages
reconcile.go New ReconcileService with diffPlanToReconcileResult conversion, filterManifestSections for scoped reconciliation
grpc_handler.go Add ReconcileManifest handler, NewHistoryHandlerWithReconcile constructor
manifest_history.go (service) Wire ReconcileService into RegisterManifestHistoryService

Drift type mapping

The differ compares stored (old) vs live (new):

  • DELETE action (in stored but not live) maps to DRIFT_TYPE_MISSING
  • CREATE action (in live but not stored) maps to DRIFT_TYPE_EXTRA
  • UPDATE action (both exist but differ) maps to DRIFT_TYPE_MODIFIED

Test plan

  • Unit tests for NewReconcileService constructor validation (nil history, nil exporter, nil differ)
  • Unit tests for diffPlanToReconcileResult with all drift types (missing, extra, modified, mixed, no drift)
  • Unit tests for toDriftTypeProto enum conversion
  • Unit tests for ReconcileResult.ToProtoResponse serialization
  • Unit tests for filterManifestSections section filtering
  • gRPC handler tests (nil reconciler returns Unimplemented, constructor wiring)
  • End-to-end tests using real ManifestDiffer with stored vs live manifests
  • All existing control-plane tests pass in short mode

Add ReconcileManifest RPC to ManifestHistoryService that compares a
stored manifest version against live service state and reports any drift
as structured output. This is a read-only safety-net operation with no
auto-repair in Phase 1.

Key changes:
- Add ReconcileManifest RPC, DriftItem, DriftType enum, and
  ReconcileSummary to manifest_history_service.proto
- Add ReconcileService that reuses ExportService for live state and
  ManifestDiffer for comparison, mapping diff actions to drift types
  (DELETE->MISSING, CREATE->EXTRA, UPDATE->MODIFIED)
- Add ReconcileManifest gRPC handler with section filtering support
- Wire ReconcileService into RegisterManifestHistoryService when
  ExportCollectors are available
- Add filterManifestSections for scoped reconciliation
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

Adds a read-only manifest reconciliation feature: new ReconcileManifest RPC, a ReconcileService that compares stored manifests to live exported state and produces per-resource drift items and an aggregate summary, handler wiring to expose the RPC, and comprehensive tests.

Changes

Cohort / File(s) Summary
Protocol Buffers
api/proto/meridian/control_plane/v1/manifest_history_service.proto
Added ReconcileManifest RPC and new messages: ReconcileManifestRequest, ReconcileManifestResponse, DriftItem, ReconcileSummary, plus DriftType enum.
Reconciliation Core Logic
services/control-plane/internal/manifest/reconcile.go
New ReconcileService, result/models (ReconcileResult, DriftItem, ReconcileSummary), Reconcile flow (load stored manifest, export live state, optional section filtering, diff, convert to result), proto conversion, and error handling.
GRPC Handler
services/control-plane/internal/manifest/grpc_handler.go
Added reconciler *ReconcileService field, NewHistoryHandlerWithReconcile constructor, and HistoryHandler.ReconcileManifest RPC handler that delegates to the reconciler and maps errors to gRPC statuses.
Service Integration
services/control-plane/service/manifest_history.go
When exporters are configured, constructs ReconcileService and uses NewHistoryHandlerWithReconcile to wire the reconciler into the manifest history handler.
Tests
services/control-plane/internal/manifest/reconcile_test.go
Extensive unit and integration-style tests covering service construction, diff-plan → drift conversion, proto mappings, section filtering, handler behavior, and end-to-end reconcile scenarios with the differ.

Sequence Diagram

sequenceDiagram
    participant Client
    participant HistoryHandler
    participant ReconcileService
    participant HistoryService
    participant ExportService
    participant Differ
    participant ProtoConverter

    Client->>HistoryHandler: ReconcileManifest(version, includeSections)
    HistoryHandler->>ReconcileService: Reconcile(ctx, version, includeSections)
    ReconcileService->>HistoryService: Load stored manifest (version)
    HistoryService-->>ReconcileService: Stored manifest
    ReconcileService->>ExportService: Export live state
    ExportService-->>ReconcileService: Live manifest
    ReconcileService->>ReconcileService: Filter stored manifest (includeSections)
    ReconcileService->>Differ: Diff(filtered stored, live)
    Differ-->>ReconcileService: DiffPlan
    ReconcileService->>ProtoConverter: ToProtoResponse(diff results)
    ProtoConverter-->>ReconcileService: ReconcileManifestResponse
    ReconcileService-->>HistoryHandler: ReconcileManifestResponse
    HistoryHandler-->>Client: ReconcileManifestResponse
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: implementing a new ReconcileManifest RPC for drift detection between stored and live manifest states.
Description check ✅ Passed The description is well-structured and directly related to the changeset, providing a clear summary, detailed file-by-file changes, drift type mappings, and a comprehensive test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 045-manifest-source-of-truth--15--reconcile-manifest-rpc
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

claude[bot]
claude Bot previously requested changes Mar 16, 2026
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See summary comment. 1 must-fix, 1 suggestion.

Comment thread services/control-plane/internal/manifest/reconcile.go
Comment thread services/control-plane/internal/manifest/reconcile.go
@claude
Copy link
Copy Markdown

claude Bot commented Mar 16, 2026

Claude Code Review

Commit: b8e42314f318136f867c09664efb39334a0c48ab | CI: still running (lint, tests, build pending; proto checks passing)

Summary

Clean implementation of the ReconcileManifest RPC for Phase 1 drift detection per PRD 045. The design correctly reuses ExportService for live state collection and ManifestDiffer for comparison, with a clear mapping from diff actions to drift types. The second commit addressed both findings from the initial review (missing reconciled_at serialization and incorrect sentinel error).

This satisfies the PRD 045 requirement: "ReconcileManifest: Compares the DB-stored manifest against live resource state across services. Reports drift."

What's good:

  • Drift type mapping (DELETE→MISSING, CREATE→EXTRA, UPDATE→MODIFIED) is correct and well-documented
  • Read-only design with no auto-repair matches Phase 1 scope
  • Graceful degradation: nil reconciler returns Unimplemented, consistent with the ExportManifest pattern
  • Immutability discipline respected: filterManifestSections returns a new manifest copy
  • Thorough test coverage: constructor validation, all drift types, mixed scenarios, proto conversion, section filtering, and end-to-end tests with real differ

Risk Assessment

Area Level Detail
Blast radius Low New read-only RPC, no existing behavior modified
Rollback Safe Additive proto change + new Go code, revert is clean
Scale Low Bounded by manifest size (typically hundreds of resources)
Cross-system Low Reuses existing ExportService collectors; no new service dependencies
Migration N/A No database migrations

Findings

Severity Location Description Status
Note reconcile.go:106 time.Now() in service method — acceptable for read-only timestamps, but makes deterministic testing harder if Reconcile() integration tests are added later. Consider injecting a clock when that happens. Informational

Bot Review Notes

CodeRabbit: Validate include_sections against supported section set (manifest_history_service.proto:349)
I reviewed this concern. If an invalid section name is passed, both filterManifestSections and ExportService.Export will filter to empty — producing a misleading "0 drift" result rather than an error. However, this is the same behavior as the existing ExportManifest RPC (consistent pattern). Server-side validation of section names would be a good future improvement across both RPCs, but it's not a correctness bug for this PR.

CodeRabbit: Don't diff a partial export as if it were complete live state (reconcile.go:125)
I reviewed this concern. If an export collector is unreachable, the diff will proceed with partial data and may report false MISSING drift. The warnings from the export are propagated to the response, so callers can detect this. For a Phase 1 read-only safety-net operation, this is acceptable — the warnings provide the necessary context. A future enhancement could add a partial: true flag or fail-fast mode.

CodeRabbit: reconciled_at is never serialized (reconcile.go:270)
Already fixed in commit b8e42314ToProtoResponse() now populates ReconciledAt: timestamppb.New(r.ReconciledAt).

Previously Flagged

Severity Location Description Status
Critical reconcile.go:244 ToProtoResponse() never populated reconciled_at Resolved in b8e4231
Improvement reconcile.go:78 Used ErrNilRepository instead of ErrHistoryServiceRequired Resolved in b8e4231

- Populate reconciled_at timestamp in ToProtoResponse
- Use ErrHistoryServiceRequired sentinel instead of ErrNilRepository
  for nil history parameter validation
@bjcoombs bjcoombs dismissed claude[bot]’s stale review March 16, 2026 13:21

Both issues addressed in b8e4231: populated reconciled_at in ToProtoResponse and switched to ErrHistoryServiceRequired sentinel.

coderabbitai[bot]
coderabbitai Bot previously requested changes Mar 16, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/proto/meridian/control_plane/v1/manifest_history_service.proto`:
- Around line 339-349: Validate the repeated string field include_sections (in
manifest_history_service.proto) against the canonical set of supported section
names (the same enum/strings used by ExportManifestRequest and the reconcile
logic) rather than accepting arbitrary strings: add proto-level validation if
possible (e.g., replace/augment repeated string with a repeated enum or use a
custom buf.validate rule) or enforce this in the server-side handler that
processes include_sections (e.g., the reconcile function) to reject the request
when an unknown name appears and return a clear validation error, and
additionally fail the request if none of the requested sections resolve so a
typo (like "instrument" vs "instruments") does not silently produce a no-op
success.

In `@services/control-plane/internal/manifest/reconcile.go`:
- Around line 100-125: The code currently diffs exportResult.Manifest
unconditionally; detect partial/incomplete exports first (e.g., inspect
exportResult.Partial or exportResult.IncompleteSections—or if the exporter
signals incompleteness only via exportResult.Warnings, detect relevant
unreachable/partial warnings) and then either fail the reconcile with a clear
error (return fmt.Errorf("export incomplete: %v", exportResult.Warnings) ) or
remove the incomplete sections from exportResult.Manifest by calling
filterManifestSections on exportResult.Manifest (similar to how filteredStored
is produced) before calling s.d.Diff; update the call sites referencing
exportResult.Manifest, exportResult.Warnings, s.d.Diff, filterManifestSections,
and includeSections accordingly so the differ never sees a partial live
manifest.
- Around line 247-270: The ToProtoResponse method on ReconcileResult is dropping
the ReconciledAt timestamp, so update ReconcileResult.ToProtoResponse to set
resp.ReconciledAt = timestamppb.New(r.ReconciledAt) (or nil-check if zero) when
building the controlplanev1.ReconcileManifestResponse; add the required
"google.golang.org/protobuf/types/known/timestamppb" import and update or add a
unit test asserting the response.ReconciledAt is populated after calling
Reconcile() to ensure the field is serialized.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9038d9fc-3875-44ce-80c6-88ba6af1d49f

📥 Commits

Reviewing files that changed from the base of the PR and between 877ae03 and 5e738e8.

📒 Files selected for processing (5)
  • api/proto/meridian/control_plane/v1/manifest_history_service.proto
  • services/control-plane/internal/manifest/grpc_handler.go
  • services/control-plane/internal/manifest/reconcile.go
  • services/control-plane/internal/manifest/reconcile_test.go
  • services/control-plane/service/manifest_history.go

Comment on lines +339 to +349
// include_sections filters which manifest sections to reconcile.
// If empty, all sections are reconciled. Valid values match ExportManifestRequest.
repeated string include_sections = 2 [(buf.validate.field).repeated = {
max_items: 20
items: {
string: {
min_len: 1
max_len: 64
}
}
}];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate include_sections against the supported section set.

This field currently accepts arbitrary strings, and the reconcile path silently drops unknown names. A typo like "instrument" instead of "instruments" will reconcile zero sections and return a clean result, which is a false negative for a drift detector. Please reject unknown values here, or fail the request when none of the requested sections resolve.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/proto/meridian/control_plane/v1/manifest_history_service.proto` around
lines 339 - 349, Validate the repeated string field include_sections (in
manifest_history_service.proto) against the canonical set of supported section
names (the same enum/strings used by ExportManifestRequest and the reconcile
logic) rather than accepting arbitrary strings: add proto-level validation if
possible (e.g., replace/augment repeated string with a repeated enum or use a
custom buf.validate rule) or enforce this in the server-side handler that
processes include_sections (e.g., the reconcile function) to reject the request
when an unknown name appears and return a clear validation error, and
additionally fail the request if none of the requested sections resolve so a
typo (like "instrument" vs "instruments") does not silently produce a no-op
success.

Comment on lines +100 to +125
// Export live state using the same section filter.
exportResult, err := s.exporter.Export(ctx, includeSections, storedVersion)
if err != nil {
return nil, fmt.Errorf("export live state: %w", err)
}

// If include_sections is specified, zero out sections not in the filter
// on the stored manifest so the differ only sees requested sections.
filteredStored := storedManifest
if len(includeSections) > 0 {
filteredStored = filterManifestSections(storedManifest, includeSections)
}

// Diff stored (as "last-applied") against live (as "new") to detect drift.
// Stored=old, Live=new:
// - DELETE actions mean resource in stored but not live -> MISSING
// - CREATE actions mean resource in live but not stored -> EXTRA
// - UPDATE actions mean resource differs -> MODIFIED
plan, err := s.d.Diff(ctx, filteredStored, exportResult.Manifest, differ.WithSkipSafetyChecks())
if err != nil {
return nil, fmt.Errorf("diff failed: %w", err)
}

result := diffPlanToReconcileResult(plan, storedVersion)
result.ReconciledAt = time.Now().UTC()
result.Warnings = exportResult.Warnings
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't diff a partial export as if it were complete live state.

The export/reconcile contract already allows warnings for unreachable services and partial data, but this path still diffs exportResult.Manifest unconditionally and only attaches the warnings afterward. That means a collector outage can surface as DRIFT_TYPE_MISSING / DRIFT_TYPE_EXTRA instead of “unchecked”. Please fail reconciliation or explicitly exclude incomplete sections before calling the differ.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/control-plane/internal/manifest/reconcile.go` around lines 100 -
125, The code currently diffs exportResult.Manifest unconditionally; detect
partial/incomplete exports first (e.g., inspect exportResult.Partial or
exportResult.IncompleteSections—or if the exporter signals incompleteness only
via exportResult.Warnings, detect relevant unreachable/partial warnings) and
then either fail the reconcile with a clear error (return fmt.Errorf("export
incomplete: %v", exportResult.Warnings) ) or remove the incomplete sections from
exportResult.Manifest by calling filterManifestSections on exportResult.Manifest
(similar to how filteredStored is produced) before calling s.d.Diff; update the
call sites referencing exportResult.Manifest, exportResult.Warnings, s.d.Diff,
filterManifestSections, and includeSections accordingly so the differ never sees
a partial live manifest.

Comment thread services/control-plane/internal/manifest/reconcile.go
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both previous findings resolved in b8e4231. No correctness issues remain. CodeRabbit's open threads (section validation, partial export diffing) are valid quality improvements for a future PR but don't affect correctness of this read-only Phase 1 safety-net. See summary comment for full analysis.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 60.75949% with 62 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...vices/control-plane/internal/manifest/reconcile.go 64.44% 39 Missing and 9 partials ⚠️
...es/control-plane/internal/manifest/grpc_handler.go 47.36% 10 Missing ⚠️
services/control-plane/service/manifest_history.go 0.00% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
services/control-plane/internal/manifest/reconcile.go (1)

101-126: ⚠️ Potential issue | 🟠 Major

Don't diff partial live exports as authoritative state.

exportResult.Warnings is only attached after s.d.Diff(...) runs. If the export path can return partial manifests when a collector is unavailable, this will turn “unchecked” sections into false MISSING / EXTRA drift. Please reject incomplete exports or strip unchecked sections before diffing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/control-plane/internal/manifest/reconcile.go` around lines 101 -
126, The export can be partial (exportResult contains Warnings) and currently
you run s.d.Diff(...) before handling that, which causes unchecked/missing
sections to be treated as authoritative; after calling s.exporter.Export(ctx,
includeSections, storedVersion) inspect exportResult for warnings or
partial-coverage indicators and either (A) reject the export by returning an
error (e.g., fmt.Errorf("incomplete export: %v", exportResult.Warnings)) or (B)
strip out unchecked sections from exportResult.Manifest using the same
filterManifestSections helper (or a new helper that removes sections marked as
unchecked) so that s.d.Diff(ctx, filteredStored, filteredLive, ...) only
compares sections both exported and requested; adjust the use sites
diffPlanToReconcileResult and result.Warnings to preserve/report warnings but
ensure the diff is only run against the cleaned/validated manifests.
🧹 Nitpick comments (1)
services/control-plane/internal/manifest/reconcile.go (1)

203-245: Consider validating section names in the RPC handler to surface typos or invalid selections.

filterManifestSections() silently ignores unknown section names—if a client passes a typo or invalid section name (e.g., ["instuments"] instead of ["instruments"]), the function returns a manifest with only metadata, making drift detection report "no drift" when nothing was actually checked. The gRPC handler does not validate include_sections before calling Reconcile(), unlike other handlers in the file that explicitly check for invalid arguments.

Add validation in ReconcileManifest() to reject requests with unrecognized section names (similar to the InvalidArgument checks for other parameters), or explicitly document that unknown sections are silently dropped.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@services/control-plane/internal/manifest/reconcile.go` around lines 203 -
245, Add validation for unknown section names in the ReconcileManifest RPC path:
before calling filterManifestSections, call parseSections(includeSections) (or
reuse its logic) and check that every provided name maps to a known Section
constant (SectionInstruments, SectionAccountTypes, SectionValuationRules,
SectionSagas, SectionMarketData, SectionOrganizations, SectionInternalAccounts,
SectionOperationalGateway, SectionMappings, SectionPartyTypes,
SectionPaymentRails); if any name is unrecognized return a gRPC InvalidArgument
error indicating the invalid section(s) instead of proceeding silently. This
ensures requests with typos (e.g., "instuments") are rejected by
ReconcileManifest rather than filtered out by filterManifestSections.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@services/control-plane/internal/manifest/reconcile.go`:
- Around line 101-126: The export can be partial (exportResult contains
Warnings) and currently you run s.d.Diff(...) before handling that, which causes
unchecked/missing sections to be treated as authoritative; after calling
s.exporter.Export(ctx, includeSections, storedVersion) inspect exportResult for
warnings or partial-coverage indicators and either (A) reject the export by
returning an error (e.g., fmt.Errorf("incomplete export: %v",
exportResult.Warnings)) or (B) strip out unchecked sections from
exportResult.Manifest using the same filterManifestSections helper (or a new
helper that removes sections marked as unchecked) so that s.d.Diff(ctx,
filteredStored, filteredLive, ...) only compares sections both exported and
requested; adjust the use sites diffPlanToReconcileResult and result.Warnings to
preserve/report warnings but ensure the diff is only run against the
cleaned/validated manifests.

---

Nitpick comments:
In `@services/control-plane/internal/manifest/reconcile.go`:
- Around line 203-245: Add validation for unknown section names in the
ReconcileManifest RPC path: before calling filterManifestSections, call
parseSections(includeSections) (or reuse its logic) and check that every
provided name maps to a known Section constant (SectionInstruments,
SectionAccountTypes, SectionValuationRules, SectionSagas, SectionMarketData,
SectionOrganizations, SectionInternalAccounts, SectionOperationalGateway,
SectionMappings, SectionPartyTypes, SectionPaymentRails); if any name is
unrecognized return a gRPC InvalidArgument error indicating the invalid
section(s) instead of proceeding silently. This ensures requests with typos
(e.g., "instuments") are rejected by ReconcileManifest rather than filtered out
by filterManifestSections.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c4ac6072-a1ff-40b3-9d23-a7f0155190cb

📥 Commits

Reviewing files that changed from the base of the PR and between 5e738e8 and b8e4231.

📒 Files selected for processing (2)
  • services/control-plane/internal/manifest/reconcile.go
  • services/control-plane/internal/manifest/reconcile_test.go

@bjcoombs bjcoombs dismissed coderabbitai[bot]’s stale review March 16, 2026 13:31

All issues addressed: reconciled_at fixed in b8e4231. include_sections validation and partial-export behavior are consistent with existing ExportManifest design.

@bjcoombs bjcoombs merged commit c3a23bf into develop Mar 16, 2026
50 of 51 checks passed
@bjcoombs bjcoombs deleted the 045-manifest-source-of-truth--15--reconcile-manifest-rpc branch March 16, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant